Skip to content

feat(security)!: secure-by-default requireAuth flip + RBAC gates for transfer/restore/purge (#2561 P0) - #2562

Merged
os-zhuang merged 4 commits into
mainfrom
claude/production-launch-priorities-ps8hk3
Jul 4, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/production-launch-priorities-ps8hk3

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

概述

落地 #2561(授权全生命周期缺口地图)framework 侧的两个 P0 上线阻断项:

  1. requireAuth 安全默认翻转(ADR-0056 D2 的 enforce 阶段)— BREAKING
  2. [security][立项位] M2 权限生命周期(undelete/purge)功能与 allowRestore/allowPurge RBAC 同批建设(evaluator 已 fail-closed,allowTransfer 已 enforced) #1883 — transfer/restore/purge 的 RBAC 门预映射

#2561 的第三个 P0(ADR-0086 objectui,越权数据暴露)在 objectstack-ai/objectui 仓库,本会话无权限,需另行处理(见 issue 勾选项)。

1. requireAuth 默认翻转(BREAKING)

层 变更
spec/api/rest-server.zod.ts requireAuth default(false) → default(true)
rest/rest-server.ts normalizeConfig ?? false → ?? true
rest/rest-api-plugin.ts boot warn 只在显式 requireAuth:false 退出时触发;并修复了原实现读错嵌套路径(config.api.requireAuth 而非 config.api.api.requireAuth)导致即使开了 requireAuth 也误报 warn 的 bug
cli serve 保留刻意的豁免:无 auth tier 的 stack(无人能登录)传显式 false + warn
verify harness 去掉覆盖——整个 dogfood 套件在平台默认姿态下运行,showcase-anonymous-deny 从此证明的是默认值本身
一致性矩阵 requireAuth-default-flip:experimental → enforced(带强制点 + dogfood proof)

翻转的前置条件已满足:公开表单经声明派生的 publicFormGrant 自授权(ADR-0056 Option A,已有 dogfood 证明),share-link 校验 token 后以系统上下文读取,控制面(/auth//health//discovery)豁免。

迁移(一行):刻意公开数据的部署需显式 api: { requireAuth: false }(changeset 已含 FROM→TO 说明)。

2. #1883 — 破坏性生命周期操作 RBAC 门

  • OPERATION_TO_PERMISSION 预映射 transfer→allowTransfer / restore→allowRestore / purge→allowPurge;modifyAllRecords 超级用户旁路覆盖该类操作(对齐 Salesforce "Modify All Data" 语义,管理员不会被锁死)。
  • 操作本身仍未进 ObjectQL(roadmap M2)——但门先于功能就位:操作一旦经安全中间件调度,无对应权限位即拒绝;未映射的破坏性操作继续 fail-closed(ADR-0049)。
  • spec 描述 [EXPERIMENTAL — not enforced] → [RBAC-gated; operation pending M2];liveness 账本补三条显式条目。
  • 单测:无位拒绝 / 有位放行 / 位不跨操作泄漏 / modifyAllRecords 旁路。

验证

  • 全量 turbo 测试套件绿(129 tasks;含 spec 6669、dogfood 181、rest 198、cli/runtime/client 全部)。
  • check:liveness 门通过(全部属性已分类)。
  • 浏览器实测(真实 showcase 服务器,Playwright 驱动 Chromium):
    • 匿名 GET/POST /api/v1/data/* → 401
    • 浏览器内登录 admin@objectos.ai 后同一 URL → 200
    • 匿名公开表单 GET /forms/contact-us → 200,submit → 201;同一匿名会话直打数据 API 仍 401(grant 不外溢)
  • 测试适配:REST/client 单测直接调度 handler、测的是路由机制而非认证门,按「刻意公开部署」显式 requireAuth:false 退出(认证门由 rest-auth-gate 单测 + anonymous-deny dogfood 覆盖)。

关联

🤖 Generated with Claude Code

https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT


Generated by Claude Code

…transfer/restore/purge (#2561 P0)

Two production-launch blockers from the authorization gap map (#2561):

1. requireAuth default flip (ADR-0056 D2, enforce stage) — BREAKING
   - spec: RestApiConfigSchema.requireAuth default(false) → default(true);
     rest-server normalizeConfig mirrors (?? true). Anonymous /data/* is
     now denied (401) unless the deployment explicitly opts out.
   - rest-api-plugin: boot warning now fires only for the EXPLICIT
     requireAuth:false opt-out, and reads the correct nested config path
     (the flat read previously warned even when requireAuth was on).
   - cli serve: keeps the deliberate carve-out — auth-less stacks get an
     explicit false (nothing could authenticate against them), warned.
   - verify harness: no override — every dogfood proof now runs on the
     platform default; anonymous-deny + public-form survival prove the
     flip (publicFormGrant was the pre-req that unblocked it).
   - conformance matrix: requireAuth-default-flip experimental → enforced.
   - REST/client unit tests that dispatch handlers anonymously opt out
     explicitly (they test routing mechanics, not the auth gate).

2. #1883 — destructive lifecycle ops RBAC gate (pre-mapped)
   - permission-evaluator: OPERATION_TO_PERMISSION now maps
     transfer→allowTransfer, restore→allowRestore, purge→allowPurge;
     modifyAllRecords super-user bypass covers the class. Ops still do
     not exist in ObjectQL (M2) — but there is no ungated window when
     they ship; unmapped destructive ops keep failing closed (ADR-0049).
   - spec: bits re-described [EXPERIMENTAL — not enforced] →
     [RBAC-gated; operation pending M2]; liveness ledger entries added.
   - unit tests: deny-without-bit / allow-with-bit / no-cross-leak /
     modifyAllRecords bypass.

Verified: full turbo test suite green (129 tasks); browser-driven
showcase run proves anonymous 401, authenticated 200, anonymous public
form 200/201 with the data API still closed to the same session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
@vercel

vercel Bot commented Jul 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 4, 2026 8:05am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 4, 2026
@github-actions

github-actions Bot commented Jul 4, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 8 package(s): @objectstack/cli, @objectstack/client, @objectstack/dogfood, @objectstack/plugin-dev, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec, @objectstack/verify.

97 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/cloud-artifact-api.mdx (via packages/cli, packages/spec)
  • content/docs/concepts/cluster-semantics.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/cli, @objectstack/client, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec, @objectstack/verify)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/concepts/packages.mdx (via @objectstack/cli, @objectstack/client, @objectstack/plugin-dev, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec)
  • content/docs/concepts/setup-app.mdx (via @objectstack/spec)
  • content/docs/concepts/skills.mdx (via @objectstack/spec)
  • content/docs/concepts/webhook-delivery.mdx (via @objectstack/spec)
  • content/docs/getting-started/architecture.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli, @objectstack/plugin-security, @objectstack/spec)
  • content/docs/getting-started/core-concepts.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/guides/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/guides/ai-capabilities.mdx (via @objectstack/spec)
  • content/docs/guides/airtable-dashboard-analysis.mdx (via @objectstack/spec)
  • content/docs/guides/analytics-datasets.mdx (via @objectstack/spec)
  • content/docs/guides/api-reference.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/guides/authentication.mdx (via @objectstack/cli, @objectstack/client)
  • content/docs/guides/business-logic.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/error-catalog.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-type-gallery.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-validation-rules.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/permissions-matrix.mdx (via packages/plugins/plugin-security, @objectstack/spec)
  • content/docs/guides/cheatsheets/protocol-diagram.mdx (via packages/spec)
  • content/docs/guides/cheatsheets/query-cheat-sheet.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/quick-reference.mdx (via @objectstack/spec)
  • content/docs/guides/client-sdk.mdx (via @objectstack/cli, @objectstack/client, @objectstack/spec)
  • content/docs/guides/common-patterns.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/auth-service.mdx (via packages/spec)
  • content/docs/guides/contracts/cache-service.mdx (via packages/spec)
  • content/docs/guides/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/index.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/guides/contracts/storage-service.mdx (via packages/spec)
  • content/docs/guides/data-modeling.mdx (via @objectstack/spec)
  • content/docs/guides/driver-configuration.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/guides/external-datasources.mdx (via @objectstack/spec)
  • content/docs/guides/formula.mdx (via @objectstack/spec)
  • content/docs/guides/hook-bodies.mdx (via packages/cli, packages/spec)
  • content/docs/guides/kernel-services.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/dashboard.mdx (via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/guides/metadata/field.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/flow.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/index.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/object.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/validation.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/workflow.mdx (via @objectstack/spec)
  • content/docs/guides/packages.mdx (via @objectstack/cli, @objectstack/client, @objectstack/plugin-dev, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec)
  • content/docs/guides/plugin-development.mdx (via @objectstack/spec)
  • content/docs/guides/plugins.mdx (via @objectstack/plugin-security, @objectstack/rest, @objectstack/spec)
  • content/docs/guides/project-scoping.mdx (via @objectstack/cli, @objectstack/client, @objectstack/spec)
  • content/docs/guides/public-forms.mdx (via @objectstack/spec)
  • content/docs/guides/runtime-services/data-service.mdx (via packages/cli, packages/client)
  • content/docs/guides/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/index.mdx (via packages/cli, packages/client, packages/spec)
  • content/docs/guides/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/guides/security.mdx (via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/guides/seed-data.mdx (via @objectstack/spec)
  • content/docs/guides/skills.mdx (via packages/cli, packages/client, @objectstack/spec)
  • content/docs/guides/solutions/approval-workflow.mdx (via packages/spec)
  • content/docs/guides/solutions/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/guides/solutions/data-automation-interface-access.mdx (via packages/plugins/plugin-security)
  • content/docs/guides/solutions/role-based-interfaces.mdx (via packages/plugins/plugin-security)
  • content/docs/guides/standards.mdx (via @objectstack/spec)
  • content/docs/guides/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/guides/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/objectos/realtime-protocol.mdx (via @objectstack/cli, @objectstack/client)
  • content/docs/protocol/objectos/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…ssion docs

The permissions-matrix cheatsheet and ObjectQL security protocol page listed
allowTransfer/allowRestore/allowPurge without noting the operations do not
exist yet (roadmap M2) — per the declared≠enforced honesty rule, both now
carry the pending-M2 / gate-pre-mapped caveat (#1883). Also documents the
modifyAllRecords bypass extension to the lifecycle class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
…in ADR-0066 future refinements

Two findings from the 2026-07 pre-launch authorization assessment that had
no ADR home:
- ⑧ runtime FLS is block-list + most-permissive union — undeclared fields
  are visible by default and a field-level deny cannot be expressed until
  the muting layer (⑦) covers field grants;
- ⑨ capability strings need an authoring/publish-gate lint when the D1
  registry lands (typos currently fail closed but undiscoverably).

Companion engineering issue (not ADR material): #2565 — surface swallowed
dbLoader failures in resolvePermissionSets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
… destructive-op gates

Correctness (record-level enforcement for the pre-mapped destructive ops):
- security-plugin middleware: extend the write-scope stash, the #1994
  pre-image RLS check, and the ADR-0055 controlled-by-parent write gate to
  cover transfer/restore/purge — the object-level RBAC bit was gated but the
  row-level defenses only listed update/delete/insert, so a granted user
  could have destroyed out-of-scope rows by id when the M2 ops ship. purge
  maps onto the delete RLS class, transfer/restore onto update. New unit
  test proves a not-owned purge is denied by the pre-image check.

Callsite coverage for the default flip:
- cli serve: carve-out now keys on (tierEnabled('auth') || hasAuthPlugin) —
  a stack mounting AuthPlugin explicitly under a minimal tier no longer gets
  an accidental fail-open override.
- plugin-dev: mirror the serve carve-out — an auth-less dev stack gets an
  explicit requireAuth:false so the local data API isn't bricked.
- spec: declare ObjectStackDefinitionSchema.api so the documented opt-out
  (and the enableProjectScoping/projectResolution/enforceProjectMembership
  knobs serve.ts already reads) survives defineStack strict parsing instead
  of being silently stripped. Verified: defineStack now returns the api key.
- rest-api-plugin: warn only on an explicit opt-out; add a misplaced-key
  guard for a flat api.requireAuth (silently ignored under the deny default).
- fix two in-tree tests that passed requireAuth at the wrong nesting.

Cleanup:
- derive the modifyAllRecords write-bypass key set from OPERATION_TO_PERMISSION
  + DESTRUCTIVE_OPERATIONS (module-level Set, no per-call array) so a future
  destructive op is covered automatically.

Docs / provenance:
- regenerate the auto-gen references (requireAuth + allow*{Transfer,Restore,
  Purge} describe text); CHANGELOG entry for the breaking flip; changeset
  migration note updated to the defineStack-level api opt-out + scope note.

Pre-existing anonymous-posture gap on /meta, dispatcher /graphql, and raw
hono /data (surfaces that never call enforceAuth) filed as #2567 — out of
scope for this flip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
@github-actions github-actions Bot added size/l and removed size/m labels Jul 4, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 4, 2026 08:18
@os-zhuang
os-zhuang merged commit 7c09621 into main Jul 4, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/production-launch-priorities-ps8hk3 branch July 4, 2026 08:25

This branch was successfully deployed

1 active deployment
Preview — ff4957c9 Deployed Jul 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants